package web

import (
	"errors"
	"geektime-go/week2/homework/domain"
	"geektime-go/week2/homework/repository/dao"
	"geektime-go/week2/homework/service"
	regexp "github.com/dlclark/regexp2"
	"github.com/gin-gonic/gin"
	"github.com/jinzhu/copier"
	"net/http"
	"strconv"
	"time"
)

type UserInterface interface {
	SignUp(ctx *gin.Context)
	Login(ctx *gin.Context)
	Edit(ctx *gin.Context)
	Profile(ctx *gin.Context)
}

type UserServer struct {
	service *service.UserService
	UserInterface
}

func NewUserServer(service *service.UserService) *UserServer {
	return &UserServer{
		service: service,
	}
}

func (server *UserServer) RegisterRoutes(engine *gin.Engine) {
	// 分组注册
	ug := engine.Group("/users")
	ug.POST("/signup", server.SignUp)
	ug.POST("/login", server.Login)
	ug.POST("/edit", server.Edit)
	ug.GET("/profile", server.Profile)
}

func (server *UserServer) SignUp(ctx *gin.Context) {
	type SignUpReq struct {
		Email           string `json:"email"`
		Password        string `json:"password"`
		ConfirmPassword string `json:"confirmPassword"`
	}
	var req SignUpReq
	if err := ctx.Bind(&req); err != nil {
		return
	}

	emailRegex := regexp.MustCompile("^\\w+([-+.]\\w+)*@\\w+([-.]\\w+)*\\.\\w+([-.]\\w+)*$", regexp.None)
	passwordRegex := regexp.MustCompile(`^(?=.*[A-Za-z])(?=.*\d)(?=.*[$@$!%*#?&])[A-Za-z\d$@$!%*#?&]{8,}$`, regexp.None)

	isEmail, err := emailRegex.MatchString(req.Email)
	if err != nil {
		ctx.String(http.StatusOK, "系统错误")
		return
	}
	if !isEmail {
		ctx.String(http.StatusOK, "邮箱不正确")
		return
	}

	if req.Password != req.ConfirmPassword {
		ctx.String(http.StatusOK, "两次输入的密码不相同")
		return
	}

	isPassword, err := passwordRegex.MatchString(req.Password)
	if err != nil {
		ctx.String(http.StatusOK, "系统错误")
		return
	}
	if !isPassword {
		ctx.String(http.StatusOK,
			"密码必须包含数字、特殊字符，并且长度不能小于 8 位")
		return
	}
	var userDomain domain.User
	err = copier.Copy(&userDomain, &req)
	if err != nil {
		ctx.String(http.StatusOK, "系统错误")
		return
	}
	err = server.service.SignUp(userDomain)

	if errors.Is(err, dao.ErrDuplicateEmail) {
		ctx.String(http.StatusOK, "重复邮箱，请换一个邮箱")
		return
	}
	if err != nil {
		ctx.String(http.StatusOK, "服务器异常，注册失败")
		return
	}
	ctx.String(http.StatusOK, "注册成功")
}

func (server *UserServer) Login(ctx *gin.Context) {
	type LoginReq struct {
		Email    string `json:"email"`
		Password string `json:"password"`
	}
	var req LoginReq
	if err := ctx.Bind(&req); err != nil {
		return
	}

	_, err := server.service.Login(req.Email, req.Password)
	if errors.Is(err, service.ErrInvalidUserOrPassword) {
		ctx.String(http.StatusOK, "邮箱或者密码不正确")
		return
	}
	if err != nil {
		ctx.String(http.StatusOK, "服务器异常，登录失败")
		return
	}
	ctx.JSON(http.StatusOK, "登录成功")
}

func (server *UserServer) Edit(ctx *gin.Context) {
	type EditReq struct {
		Id           int64  `json:"id"`
		NickName     string `json:"nickName"`
		BirthDay     string `json:"birthDay"`
		Introduction string `json:"introduction"`
	}
	type Result struct {
		Code int
		Msg  string
	}
	var req EditReq
	if err := ctx.Bind(&req); err != nil {
		return
	}

	if len(req.NickName) > 32 {
		ctx.JSON(http.StatusOK, Result{Code: 4, Msg: "昵称不能超过 32 个字符"})
		return
	}

	if len(req.Introduction) > 1024 {
		ctx.JSON(http.StatusOK, Result{Code: 4, Msg: "个人简介不能超过 1024 个字符"})
		return
	}
	birthday, err := time.Parse(time.DateOnly, req.BirthDay)
	if err != nil {
		ctx.JSON(http.StatusOK, Result{Code: 4, Msg: "日期格式错误"})
		return
	}

	err = server.service.Edit(domain.User{
		Id:           req.Id,
		NickName:     req.NickName,
		BirthDay:     birthday,
		Introduction: req.Introduction,
	})
	if err != nil {
		ctx.String(http.StatusOK, err.Error())
		return
	}
	ctx.String(http.StatusOK, "编辑成功")
}

func (server *UserServer) Profile(ctx *gin.Context) {
	type Profile struct {
		Email        string
		NickName     string
		Birthday     string
		Introduction string
	}
	id := ctx.Query("id")
	idInt, err := strconv.Atoi(id)
	if err != nil {
		ctx.String(http.StatusOK, "id格式错误")
		return
	}
	user, err := server.service.Profile(int64(idInt))
	if err != nil {
		ctx.String(http.StatusOK, "系统错误")
		return
	}
	ctx.JSON(http.StatusOK, Profile{
		Email:        user.Email,
		NickName:     user.NickName,
		Birthday:     user.BirthDay.Format(time.DateOnly),
		Introduction: user.Introduction,
	})
}
